home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Games of Daze
/
Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso
/
x2ftp
/
msdos
/
docs
/
pasctxt
/
chap11.txt
< prev
next >
Wrap
Text File
|
1988-01-15
|
30KB
|
653 lines
CHAPTER 11 - Files
One of the most common operations when using a computer
is to either read from, or write to a file. You are already
somewhat experienced in file handling from the last chapter,
because in computer terminology, the keyboard, terminal, and
printer are all classified as files. A file is any serial
input or output device that the computer has access to.
Since it is serial, only one piece of information is
available to the computer at any instant of time. This is in
contrast to an array, for example, in which all elements of
the array are stored internally and are all available at any
time.
A SHORT HISTORY LESSON
Several years ago computers were all large cumbersome
machines with large peripheral devices such as magnetic tape
drives, punch card readers, paper tape readers or punches,
etc. It was a simple task to assign the paper tape reader a
symbol and use that symbol whenever it was necessary to read
a paper tape. There was never more than one file on the
paper tape being read, so it was simply read sequentially,
and hopefully the data was the desired data. With the
advent of floppy disks, and hard disks, it became practical
to put several files of data on one disk, none of which
necessarily had anything to do with any of the other files
on that disk. This led to the problem of reading the proper
file from the disk, not just reading the disk.
Pascal was originally released in 1971, before the
introduction of the compact floppy disk. The original
release of Pascal had no provision for selecting a certain
file from among the many included on the disk. Each
compiler writer had to overcome this deficiency and he did
so by defining an extension to the standard Pascal system.
Unfortunately, all of the extensions were not the same, and
there are now several ways to accomplish this operation.
There are primarily two ways, one using the Assign
statement, and the other using the Open statement. They are
similar to each other and they accomplish the same end
result.
BACK TO THE PRESENT TIME
All of the above was described to let you know that we
will have a problem in this chapter, namely, how do we cover
all of the possible implementations of Pascal available?
The answer is, we can't. Most of what is covered in this
chapter will apply to all compilers, and all that is covered
will apply to the TURBO Pascal compilers, versions 3.0 and
4.0. If you are not using TURBO Pascal and your compiler
complains about some of the statements, it will be up to you
Page 63
CHAPTER 11 - Files
to dig out the details of how to do the intended operations.
If any one or more of these operations cannot be
accomplished with your compiler, you should seriously
consider purchasing a better compiler because all of these
operations are needed in a useful Pascal environment.
READING AND DISPLAYING A FILE
Examine the file READFILE for an example of a program
that can read a text file from the disk. In fact it will
read itself from the disk and display it on the video
monitor. The first statement in the program is the Assign
statement. This is TURBO Pascal's way of selecting which
file on the disk will be either read from or written to. In
this case we will read from the disk. The first argument in
the Assign statement is the device specifier similar to Lst
used in the last chapter for the printer. We have chosen to
use the name Turkey for the device identifier, but could
have used any valid identifier. This identifier must be
defined in a var declaration as a TEXT type variable. The
next argument is the filename desired. The filename can be
defined as a string constant, as it is here, or as a string
variable.
The "TEXT" type is a predefined type and is used to
define a file identifier. It is predefined as a "file of
char", so it can only be used for a text file. We will see
later that there is another type of file, a binary file.
Now that we have a file identified, it is necessary to
prepare it for reading by executing a reset statement in
line 9. The reset statement positions the read pointer at
the beginning of the file ready to read the first piece of
information in the file. Once we have done that, data is
read from the file in the same manner as it was when reading
from the keyboard. In this program, the input is controlled
by the while loop which is executed until we exhaust the
data in the file.
WHAT ARE THE "EOF" AND "EOLN" FUNCTIONS?
The Eof function is new and must be defined. When we
read data from the file, we move closer and closer to the
end, until finally we reach the end and there is no more
data to read. This is called "end of file" and is
abbreviated Eof. Pascal has this function available as a
part of the standard library which returns FALSE until we
reach the last line of the file. When there is no more data
to read left in the file, the function Eof returns TRUE. To
use the function, we merely give it our file identifier as
Page 64
CHAPTER 11 - Files
an argument. It should be clear that we will loop until we
read all of the data available in the input file.
The Eoln function is not used in this program but is a
very useful function. If the input pointer is anywhere in
the text file except at the end of a line, the Eoln returns
FALSE, but at the end of a line, it returns a value of TRUE.
This function can therefore be used to find the end of a
line of text for variable length text input.
To actually read the data, we use the Readln procedure,
giving it our identifier Turkey and the name of the variable
we want the data read into. In this case, we read up to 80
characters into the string and if more are available, ignore
them. You should remember when we did this in the last
chapter from the keyboard input. We are using the same
technique here except we are reading from a file this time.
Since we would like to do something with the data, we output
the line to the default device, the video monitor. It
should be clear to you by now that the program will read the
entire file and display it on the monitor.
Finally, we Close the file Turkey. It is not really
necessary to close the file because the system will close it
for you automatically at program termination, but it is a
good habit to get into. It must be carefully pointed out
here, that you did not do anything to the input file, you
only read it and left it intact. You could Reset it and
reread it again in this same program.
Compile and run this program to see if it does what you
expect it to do.
A PROGRAM TO READ ANY FILE
Examine the next program READDISP for an improved file
reading program. This is very similar except that it asks
you for the name of the file that you desire to display, and
enters the name into a 12 character string named
Name_Of_File_To_Input. This is then used in the Assign
statement to select the file to be read, and the file is
reset as before. Lines 15 through 18 display a header, and
from that point on, the program is identical to the last one
with a few small additions. In order to demonstrate the use
of a function within the Writeln specification, the program
calls for the length of the input string in line 23 and
displays it before each line. The lines are counted as they
are read and displayed, and the line count is displayed at
the end of the listing.
Page 65
CHAPTER 11 - Files
You should be able to see clearly how each of these
operations is accomplished. Compile and run this program,
entering any filename we have used so far (be sure to
include the .PAS extension). After a successful run, enter
a nonexistent filename and see the I/O error.
HOW TO COPY A FILE (SORT OF)
Examine the file READSTOR for an example of both
reading from a file and writing to another one. In this
program we request an operator input for the filename to
read, after which we Assign the name to the file and Reset
it. When we reset the file however, we go to a bit of extra
trouble to assure that the file actually exists.
Suppose we input a filename, and the file did not exist
because the file was actually missing, or because we entered
the filename wrong. Without the extra effort, the TURBO
Pascal runtime system would indicate a run-time error, and
terminate the program returning us to the operating system.
In order to make a program easier to use, it would be nice
to tell the operator that the file didn't exist and give him
the opportunity to try again with another file name. The
method given in lines 16 through 20 of this program will
allow you to do just that.
USING A COMPILER DIRECTIVE
First you must disable the built in TURBO Pascal I/O
checking by inserting the compiler directive in line 16.
This tells the system to ignore any I/O errors from this
point on and if the file doesn't exist, the system will not
abort when you attempt to reset it in line 17. Another
compiler directive is given in line 18 to enable I/O
checking again for the remainder of the program.
WE DO OUR OWN FILE CHECKING
If the file didn't exist and could not therefore be
reset, we have a problem because the program thinks the file
is available for use but it actually isn't. Fortunately,
TURBO Pascal has a built in variable, named "IOResult", that
informs us of the result of each I/O operation. Following
any I/O operation, if this variable contains the value of
zero, the I/O operation was correct, and if it contains any
other value, the operation had some sort of error. In our
case, we simply compare it to zero to generate a boolean
value, then based on the boolean value we either give an
error message and stop, or perform the desired file
operations.
Page 66
CHAPTER 11 - Files
It would be good programming practice to check all file
openings in this manner to allow the operator to recover
from a simple oversight or spelling error.
If the file was opened properly, then in line 21
through 24 we request a different filename to write to,
which is assigned to a different identifier. Note that the
output file is not checked for a valid opening as it should
be. The statement in line 24 is new to us, the Rewrite
statement. This name apparently comes from the words REset
for WRITEing because that is exactly what it does. It
clears the entire file of any prior data and prepares to
write into the very beginning of the file. Each time you
write into it, the file grows by the amount of the new data
written.
Once the identifier has been defined, and the Rewrite
has been executed, writing to the file is identical to
writing to the display with the addition of the identifier
being specified prior to the first output field. With that
in mind, you should have no trouble comprehending the
operation of the program. This program is very similar to
the last, except that it numbers the lines as the file is
copied. After running the program, look in your default
directory for the new filename which you input when it asked
for the output filename. Examine that file to see if it is
truly a copy of the input file with line numbers added.
One word of caution. If you used an existing filename
for the output file, the file was overwritten, and the
original destroyed. In that case, it was good that you
followed instructions at the beginning of this tutorial and
made a working copy of the distribution disk. You did do
that, didn't you?
Compile and run this program two different ways, once
with a valid input filename that should run properly, and
the second time with an input filename that doesn't exist to
prove to yourself that the test actually does work
correctly.
HOW TO READ INTEGER DATA FROM A FILE
It is well and good to be able to read text from a
file, but now we would like to read other forms of data from
a file. First we will look at an example program to read
data from a text file, then later we will see an example
program that reads from a binary file.
Examine the program READINTS for an example of reading
data from a text file. A text file is an ASCII file that
Page 67
CHAPTER 11 - Files
can be read by a text editor, printed, displayed, or in some
cases, compiled and executed. It is simply a file made up
of a long string of char type data, and usually includes
linefeeds, carriage returns, and blanks for neat formatting.
Nearly every file on the Tutorial disk you received with
this package is a text file. One notable exception is the
file named LIST.COM, which is an executable program file.
The example program has nothing new, you have seen
everything in it before. We have an assignment, followed by
a reset of our file, followed by four read and write loops.
Each of the loops has a subtle difference to illustrate the
Read and Readln statements. Notice that the same file is
used for reading four times with a Reset prior to each,
illustrating the nondestructive read mentioned a few
paragraphs ago.
The file we will be using is named INTDATA.TXT and is
on your disk. You could display it at this time using the
program READDISP we covered recently. Notice that it is
simply composed of the integer values from 101 to 148
arranged four to a line with a couple of spaces between each
for separation and a neat appearance. The important thing
to remember is that there are four data points per line.
READ AND READLN ARE SLIGHTLY DIFFERENT
As variables are read in with either procedure, the
input file is scanned for the variables using blanks as
delimiters. If there are not enough data points on one line
to satisfy the arguments in the input list, the next line is
searched also, and the next, etc. Finally when all of the
arguments in the input list are satisfied, the Read is
complete, but the Readln is not. If it is a Read procedure,
the input pointer is left at that point in the file, but if
it is a Readln procedure, the input pointer is advanced to
the beginning of the next line. The next paragraph should
clear that up for you.
The input data file INTDATA.TXT has four data points
per line but the first loop in the program READINTS.PAS
requests only three each time through the loop. The first
time through, it reads the values 101, 102, and 103, and
displays those values, leaving the input pointer just prior
to the 104, because it is a Read procedure. The next time
through, it reads the value 104, advances to the next line
and reads the values 105, and 106, leaving the pointer just
prior to the 107. This continues until the 5 passes through
the loop are completed.
Page 68
CHAPTER 11 - Files
The loop in lines 19 through 22 contains a Readln
procedure and also reads the values 101, 102, and 103, but
when the input parameter list is satisfied, it moves the
pointer to the beginning of the next line, leaving it just
before the 105. The values are printed out and the next
time we come to the Readln, we read the 105, 106, and 107,
and the pointer is moved to the beginning of the next line.
It would be good to run the program now to see the
difference in output data for the two loops. Remember that
the only difference is that the first loop uses the Read
procedure, and the second uses the Readln procedure.
When you come back to the program again, observe the
last two loops, which operate much like the first two except
that there are now five requested integer variables, and the
input file still only has four per line. This is no
problem. Both input procedures will simply read the first
four in the first line, advance to the second line for its
required fifth input, and each will do its own operation
next. The Read procedure will leave the input pointer just
before the second data point of the second line, and the
Readln will advance the input pointer to the beginning of
the third line. Run this program and observe the four
output fields to see an illustration of these principles.
NOW TO READ SOME REAL VARIABLES FROM A FILE
By whatever method you desire, take a look at the file
named REALDATA.TXT supplied on your Pascal Tutorial disk.
You will see 8 lines of what appears to be scrambled data,
but it is good data that Pascal can read. Notice especially
line 4 which has some data missing, and line 6 which has
some extra data.
Examine the program file READDATA which will be used to
illustrate the method of reading real type data. Everything
should be familiar to you, since there is nothing new here.
The Readln statement is requesting one integer variable, and
three real variables, which is what most of the input file
contained. When we come to the fourth line, there are not
enough data points available, so the first two data points
of the next line are read to complete the fourth pass
through the loop. Since the file pointer is advanced to the
beginning of the next line, we are automatically
synchronized with the data again. When we come to the sixth
line, the last two data points are simply ignored. Run the
program to see if the results are as you would predict.
If a Read were substituted for the Readln in line 14 of
the program, the file pointer would not be advanced to the
beginning of line 6, after the fourth pass through the loop.
Page 69
CHAPTER 11 - Files
The next attempt to read would result in trying to read the
value 0.0006 as an integer, and a run time error would
result. Modify the program, substituting a Read for the
Readln in line 14, and see if this is not true.
It should be pointed out that TURBO Pascal 4.0 requires
a digit both before and after the decimal point in all data
that is to be read in as real type data or it will be
flagged as a run-time error and the program will be halted.
The digits can be zero as they are in several places in the
example file but they must be there. If you are using TURBO
Pascal 3.0, the leading and trailing digits are not
required.
That is all there is to reading and writing text files.
If you learn the necessities, you will not be stumbling
around in the area of input/output which is very
intimidating to many people. Remember to Assign, then Reset
before reading, Rewrite before writing, and Close before
quitting. It is of the utmost importance to close a file
you have been writing to before quitting to write the last
few buffers to the file, but it is not as important to close
read files unless you are using a lot of them, as there is
an implementation dependent limit of how many files can be
open at once. It is possible to read from a file, close it,
reopen it, and write to it in one program. You can reuse a
file as often as you desire in a program, but you cannot
read from and write into a file at the same time.
NOW FOR BINARY INPUT AND OUTPUT
Examine the file BINOUT for an example of writing data
to a file in binary form. First there is a record defined
in the type declaration part composed of three different
variable types. In the var part, Output_File is defined as
a "file of Dat_Rec", the record defined earlier. The
variable Dog_Food is then defined as an array of the record,
and a simple variable is defined.
Any file assigned a type of TEXT, which is a "file of
char", is a text file. A text file can be read and modified
with a text editor, printed out, displayed on the monitor,
etc. If a file is defined with any other definition, it will
be a binary file and will be in an internal format as
defined by the Pascal compiler. Attempting to display such
a file will result in very strange looking gibberish on the
monitor.
When we get to the program, the output file is assigned
a name in line 15, and a Rewrite is performed on it to reset
the input pointer to the beginning of the file, empty the
Page 70
CHAPTER 11 - Files
file, and prepare for writing data into it. The loop in
lines 18 through 22 simply assigns nonsense data to all of
the variables in the 20 records so we have something to work
with.
We write a message to the display that we are ready to
start outputting data, and we output the data one record at
a time with the standard Write statement. A few cautions
are in order here. The output file can be defined as any
simple variable type, integer, byte, real, or a record, but
the types cannot be mixed. The record itself however, can
be any combination of data including other records if
desired, but any file can only have one type of record
written to it.
A Writeln statement is illegal when writing to a binary
file because a binary file is not line oriented. A Write
statement is limited to one output field per statement. It
is a simple matter to put one Write statement in the program
for each variable you wish to write out to the file. It is
important to Close the file when you are finished writing to
it.
WHY USE A BINARY FILE
A binary file written by a Pascal program cannot be
read by a word processor, a text editor or any other
application program such as a database or spreadsheet, and
it may not even be readable by a Pascal program compiled by
a different companies compiler because the actual data
structure is implementation dependent. It can't even be
read by a Pascal program using the same compiler unless the
data structure is identical to the one used to write the
file. With all these rules, it seems like a silly way to
output data, but there are advantages to using a binary
output.
A binary file uses less file space than a corresponding
text file because the data is stored in a packed mode.
Since all significant digits of real data are stored, it is
more precise unless you are careful to output all
significant data to the corresponding TEXT file. Finally,
since the binary data does not require formatting into ASCII
characters, it will be considerably faster than outputting
it in TEXT format. When you run this example program, it
will create the file KIBBLES.BIT, and put 20 records in it.
Return to DOS and look for this file and verify its
existence. If you try to TYPE it, using the DOS TYPE
command, you will have a real mess, but that might be a good
exercise.
Page 71
CHAPTER 11 - Files
READING A BINARY FILE
BININ is another example program that will read in the
file we just created. Notice that the variables are named
differently, but the types are all identical to those used
to write the file. An additional line is found in the
program, the if statement. We must check for the "end of
file" marker to stop reading when we find it or Pascal will
list an error and terminate operation. Three pieces of
information are written out to verify that we actually did
read the data file in.
Once again, a few rules are in order. A Readln is
illegal since there are no lines in a binary file, and only
one variable or record can be read in with each Read
statement.
WHAT ABOUT FILE POINTERS, GET, AND PUT STATEMENTS?
File pointers and the Get and Put procedures are a part
of standard Pascal, but since they are redundant, they are
not a part of TURBO Pascal. The standard Read and Write
procedures are more flexible, more efficient, and easier to
use. The use of Get and Put will not be illustrated or
defined here. If you ever have any need for them, they
should be covered in detail in your Pascal reference manual
for the particular implementation you are using.
Pointers will be covered in detail in the next chapter
of this tutorial.
PROGRAMMING EXERCISES
1. Write a program to read in any text file, and display it
on the monitor with line numbers and the number of
characters in each line. Finally display the number of
lines found in the file, and the total number of
characters in the entire file. Compare this number with
the filesize given by the DOS command DIR.
2. Write a silly program that will read two text files and
display them both on the monitor on alternating lines.
This is the same as "shuffling" the two files together.
Take care to allow them to end at different times,
inserting blank lines for the file that terminates
earlier.
Page 72